{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit VButtons;

interface

uses Classes, CaptionedObject, UIObjects, BaseControls, Fonts, Captions, XMLHelper;

{$IFDEF DESIGNER}
const
  MODIFY_STATES   = 'Edit States';
  MODIFY_ICONS    = 'Edit Icons';
  MODIFY_FONTS    = 'Edit Fonts';
  MODIFY_CAPTIONS = 'Edit Captions';

  // Property categories and hints for designer
  CAT_DESIGN = 'Design';
  CAT_APPEARANCE = 'Appearance';
  HINT_ICONS = 'defines icon bitmaps or shapes for each button state';
  HINT_STATES = 'defines background bitmaps or shapes for each button state';
  HINT_FONTS = 'defines fonts for each button state (overrides caption font)';
  HINT_CAPTIONS = 'defines captions for each button state (overrides default caption)';
  HINT_CAPTION = 'defines default caption for all button states';
  HINT_CAPTIONX = 'specifies horizontal offset for the caption position';
  HINT_CAPTIONY = 'specifies vertical offset for the caption position';
  HINT_ICONX = 'specifies horizontal offset for the icon position';
  HINT_ICONY = 'specifies vertical offset for the icon position';
  HINT_ICONWIDTH = 'specifies custom width for the icon (or 0 to use default width)';
  HINT_ICONHEIGHT = 'specifies custom height for the icon (or 0 to use default height)';
  HINT_WIDTH = 'specifies width for the button';
  HINT_HEIGHT = 'specifies height for the button';
{$ENDIF}

type
  TAHMButton = class(TAHMUICaptionedDisplayObject)
  private
    FIconX: Single;
    FIconY: Single;
    FIconWidth: Single;
    FIconHeight: Single;
    FVirtIconX: Integer;
    FVirtIconY: Integer;
    FVirtIconWidth: Integer;
    FVirtIconHeight: Integer;
    FStates: TAHMUIStateObjectList;
    FStateIcons: TAHMUIStateObjectList;
    FStateFonts: TAHMUIStateObjectList;
    FStateCaptions: TAHMUIStateObjectList;
  protected
    procedure SetVirtualIconX(X: Integer);
    procedure SetVirtualIconY(Y: Integer);
    procedure SetVirtualIconWidth(Width: Integer);
    procedure SetVirtualIconHeight(Height: Integer);
    procedure ClearProperties; override;
  public
    constructor Create(const Name: String); override;
    destructor Destroy; override;
    property IconX: Single read FIconX write FIconX;
    property IconY: Single read FIconY write FIconY;
    property IconWidth: Single read FIconWidth write FIconWidth;
    property IconHeight: Single read FIconHeight write FIconHeight;
    function ModifyActions: Integer; override;
    function Modify(Action: Integer): Boolean; override;
    function ModifyCaption(Action: Integer): String; override;
    procedure Render(Control: TObject; iX, iY, iWidth, iHeight: Single); override;
    procedure SeedObject; override;
    procedure Assign(Source: TPersistent); override;
    procedure SaveXMLBody(Lines: TStrings); override;
    procedure ParseFromXML(XML: TAHMXMLHelper); override;
    procedure InvalidateUIObject(UIObject: TAHMUIObject); override;
    procedure BuildProperties(Properties: TAHMUIPropertyList); override;
  published
    property States: TAHMUIStateObjectList read FStates;
    property StateIcons: TAHMUIStateObjectList read FStateIcons;
    property StateFonts: TAHMUIStateObjectList read FStateFonts;
    property StateCaptions: TAHMUIStateObjectList read FStateCaptions;
    property VirtualIconX: Integer read FVirtIconX write SetVirtualIconX;
    property VirtualIconY: Integer read FVirtIconY write SetVirtualIconY;
    property VirtualIconWidth: Integer read FVirtIconWidth write SetVirtualIconWidth;
    property VirtualIconHeight: Integer read FVirtIconHeight write SetVirtualIconHeight;
    // Publish inherited properties
    property Caption;
    property VirtualWidth;
    property VirtualHeight;
    property VirtualCaptionX;
    property VirtualCaptionY;
  end;

  TAHMButtonList = class(TAHMUIObjectList)
  private
  protected
  public
    constructor Create; override;
    function AddButton(const InheritFrom, NewName: String): TAHMButton;
    function GetButton(const Name: String): TAHMButton;
    procedure PopulateDefaultObjects; override;
  end;


implementation

uses SysUtils, AHMTypes, ApplicationManager, Bitmaps, Shapes, Screen, TagNames,
     XMLTags, {$IFDEF DESIGNER}DesignerStateList,{$ENDIF} ErrorCodes;

constructor TAHMButtonList.Create;
begin
  inherited Create;

  XMLTagName := TAG_BUTTONS;
  ChildClass := TAHMButton;
end;

function TAHMButtonList.AddButton(const InheritFrom, NewName: String): TAHMButton;
begin
  Result := TAHMButton(Inherit(InheritFrom, NewName));
end;

function TAHMButtonList.GetButton(const Name: String): TAHMButton;
begin
  Result := TAHMButton(GetObject(Name));
end;

procedure TAHMButtonList.PopulateDefaultObjects;
begin
  with ApplicationManagerInstance.ThemeManager, DefaultObject as TAHMButton do
  begin
    VirtualWidth := DEFAULT_CTRL_WIDTH;
    VirtualHeight := DEFAULT_CTRL_HEIGHT;
    VirtualIconX := 5 * DEFAULT_CTRL_WIDTH div 6;
    VirtualIconY := DEFAULT_CTRL_HEIGHT div 3;
    VirtualIconWidth := DEFAULT_CTRL_HEIGHT div 3;
    VirtualIconHeight := DEFAULT_CTRL_HEIGHT div 3;
    VirtualCaptionX := DEFAULT_CTRL_HEIGHT div 10;
    VirtualCaptionY := DEFAULT_CTRL_HEIGHT div 10;
    States.AddObject(STATE_VAL_IDLE, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_IDLE]);
    States.AddObject(STATE_VAL_FOCUS, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_FOCUS]);
    States.AddObject(STATE_VAL_FOCUS_CHECK, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_FOCUS]);
    States.AddObject(STATE_VAL_PRESS, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_PRESS]);
    States.AddObject(STATE_VAL_PRESS_CHECK, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_PRESS]);
    States.AddObject(STATE_VAL_DISABLE, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_DISABLE]);
    States.AddObject(STATE_VAL_DISABLE_CHECK, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_DISABLE]);
    StateIcons.AddObject(STATE_VAL_IDLE_CHECK, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_IDLE_CHECK]);
    Caption := TAHMCaption(ThemeObjects[otCaptions].DefaultObject);
  end;
end;

//----------------------------------------------------------------------------//

constructor TAHMButton.Create(const Name: String);
begin
  inherited Create(Name);

  XMLTagName := TAG_BUTTON;
  ObjectType := otButtons;
  ImageIndex := Ord(otButtons);
  PreviewControlClass := TAHMInteractiveControl;

  FStates := TAHMUIStateObjectList.Create;
  FStates.ObjectTypes := [otShapes, otBitmaps];
  FStates.Owner := Self;
  FStateIcons := TAHMUIStateObjectList.Create;
  FStateIcons.ObjectTypes := [otShapes, otBitmaps];
  FStateIcons.Owner := Self;
  FStateFonts := TAHMUIStateObjectList.Create;
  FStateFonts.ObjectTypes := [otFonts];
  FStateFonts.Owner := Self;
  FStateCaptions := TAHMUIStateObjectList.Create;
  FStateCaptions.ObjectTypes := [otCaptions];
  FStateCaptions.Owner := Self;

{$IFDEF DESIGNER}
  FStates.DisplayClassName := 'Button state';
  FStateIcons.DisplayClassName := 'Button icon';
  FStateFonts.DisplayClassName := 'Button font';
  FStateCaptions.DisplayClassName := 'Button caption';
{$ENDIF}
end;

destructor TAHMButton.Destroy;
begin
  FStates.Free;
  FStateIcons.Free;
  FStateFonts.Free;
  FStateCaptions.Free;

  inherited Destroy;
end;

procedure TAHMButton.SetVirtualIconX(X: Integer);
begin
  FVirtIconX := X;
  FIconX := ScreenInstance.ScaleVirtualX(X);
end;

procedure TAHMButton.SetVirtualIconY(Y: Integer);
begin
  FVirtIconY := Y;
  FIconY := ScreenInstance.ScaleVirtualY(Y);
end;

procedure TAHMButton.SetVirtualIconWidth(Width: Integer);
begin
  FVirtIconWidth := Width;
  FIconWidth := ScreenInstance.ScaleVirtualX(Width);
end;

procedure TAHMButton.SetVirtualIconHeight(Height: Integer);
begin
  FVirtIconHeight := Height;
  FIconHeight := ScreenInstance.ScaleVirtualY(Height);
end;

procedure TAHMButton.ClearProperties;
begin
  inherited;

  // Clear custom button properties
  FStates.Clear;
  FStateIcons.Clear;
  FStateFonts.Clear;
  FStateCaptions.Clear;
  VirtualIconX := 0;
  VirtualIconY := 0;
  VirtualIconWidth := 0;
  VirtualIconHeight := 0;
end;

function TAHMButton.ModifyActions: Integer;
begin
  // Button supports editing states, icons, fonts or captions
  Result := 4;
end;

function TAHMButton.Modify(Action: Integer): Boolean;
begin
{$IFDEF DESIGNER}
  case Action of
    0: // Edit states with list dialog
       Result := TfrmObjectStateList.ShowList(FStates);
    1: // Edit icons with list dialog
       Result := TfrmObjectStateList.ShowList(FStateIcons);
    2: // Edit fonts with list dialog
       Result := TfrmObjectStateList.ShowList(FStateFonts);
    3: // Edit captions with list dialog
       Result := TfrmObjectStateList.ShowList(FStateCaptions);
    else Result := False;
  end;

  if Result then DoModified;
{$ELSE}
  Result := False;
{$ENDIF}
end;

function TAHMButton.ModifyCaption(Action: Integer): String;
begin
{$IFDEF DESIGNER}
  case Action of
    0: Result := MODIFY_STATES;
    1: Result := MODIFY_ICONS;
    2: Result := MODIFY_FONTS;
    3: Result := MODIFY_CAPTIONS;
    else Result := '';
  end;
{$ELSE}
  Result := '';
{$ENDIF}
end;

procedure TAHMButton.Render(Control: TObject; iX, iY, iWidth, iHeight: Single);
var
  State: TAHMControlState;
  Checked: Boolean;
  oStateObject: TAHMUIDisplayObject;
  oStateIcon: TAHMUIDisplayObject;
  oStateCaption: TAHMCaption;
  oStateFont: TAHMFont;
begin
  // Get corresponding state object, font and caption for current control state
  State := TAHMInteractiveControl(Control).ControlState;
  Checked := TAHMInteractiveControl(Control).Checked;
  oStateObject := TAHMUIDisplayObject(FStates.GetStateObject(State, Checked));
  oStateIcon := TAHMUIDisplayObject(FStateIcons.GetStateObject(State, Checked));
  oStateCaption := TAHMCaption(FStateCaptions.GetStateObject(State, Checked));
  oStateFont := TAHMFont(FStateFonts.GetStateObject(State, Checked));

  // If we found a corresponding state object then tweak its size
  if Assigned(oStateObject) then
  with oStateObject do
  begin
    Width := Self.Width;
    Height := Self.Height;
    Render(Control);
  end;

  // Render icon if found
  if Assigned(oStateIcon) then
    oStateIcon.Render(Control, iX + FIconX, iY + FIconY, FIconWidth, FIconHeight);

  // Determine which font we should use - font overrides caption
  if not Assigned(oStateFont) then
    if Assigned(oStateCaption) then oStateFont := oStateCaption.Font
    else if Assigned(Caption) then oStateFont := Caption.Font;

  // Render caption using custom caption if found
  if Assigned(oStateCaption) then
    oStateCaption.RenderCustomFont(Control, oStateFont)
  else if Assigned(Caption) then
    Caption.RenderCustomFont(Control, oStateFont);
end;

procedure TAHMButton.SeedObject;
begin
{$IFDEF DESIGNER}
  if Ancestor = nil then
    with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
    begin
      VirtualWidth := DEFAULT_CTRL_WIDTH;
      VirtualHeight := DEFAULT_CTRL_HEIGHT;
      VirtualIconX := 5 * DEFAULT_CTRL_WIDTH div 6;
      VirtualIconY := DEFAULT_CTRL_HEIGHT div 3;
      VirtualIconWidth := DEFAULT_CTRL_HEIGHT div 3;
      VirtualIconHeight := DEFAULT_CTRL_HEIGHT div 3;
      VirtualCaptionX := DEFAULT_CTRL_HEIGHT div 10;
      VirtualCaptionY := DEFAULT_CTRL_HEIGHT div 10;
      States.AddObject(STATE_VAL_IDLE, ShapeList.GetShape(DEFAULT_OBJECT_NAME));
      StateIcons.AddObject(STATE_VAL_IDLE_CHECK, ShapeList.GetShape(DEFAULT_CIRCLE_NAME));
      Caption := CaptionList.GetCaption(DEFAULT_OBJECT_NAME);
    end;
{$ENDIF}
end;

procedure TAHMButton.Assign(Source: TPersistent);
begin
  inherited Assign(Source);

  with Source as TAHMButton do
  begin
    Self.States.Assign(States);
    Self.StateIcons.Assign(StateIcons);
    Self.StateFonts.Assign(StateFonts);
    Self.StateCaptions.Assign(StateCaptions);
    Self.VirtualIconX := VirtualIconX;
    Self.VirtualIconY := VirtualIconY;
    Self.VirtualIconWidth := VirtualIconWidth;
    Self.VirtualIconHeight := VirtualIconHeight;
  end;
end;

procedure TAHMButton.SaveXMLBody(Lines: TStrings);
var
  oAncs: TAHMButton;
  oObj, oIcon, oFont, oCaption: TAHMUIObject;
  i: Integer;
begin
  // Determine which ancestor to compare against - real or empty
  oAncs := TAHMButton(ObjectToCompare);

  // Only populate attributes that differ from our ancestor
  if (Caption <> nil) and (Caption <> oAncs.Caption) then
  begin
    Lines.Add(TABS[3] + TGB+TAG_CAPTION+SPC+ATTR_NAME+ATB + Caption.Name + ATC);
    // Do we need to store position?
    if ((VirtualCaptionX <> 0) and (VirtualCaptionX <> oAncs.VirtualCaptionX)) or
       ((VirtualCaptionY <> 0) and (VirtualCaptionY <> oAncs.VirtualCaptionY)) then
      Lines.Add(TABS[4] + TGB+TAG_POSITION+TGC+ IntToStr(VirtualCaptionX) + ',' +
                IntToStr(VirtualCaptionY) + TGE+TAG_POSITION+TGC);
    Lines.Add(TABS[3] + TGE+TAG_CAPTION+TGC);
  end;

  // Do we need to store states?
  if FStates.IsDifferent(oAncs.States) or
     FStateFonts.IsDifferent(oAncs.StateFonts) or
     FStateCaptions.IsDifferent(oAncs.StateCaptions) then
  begin
    Lines.Add(TABS[3] + TGB+TAG_STATES+TGC);
    for i := 0 to High(ControlStateNames) do
    begin
      oObj := FStates.GetState(ControlStateNames[i]);
      oFont := FStateFonts.GetState(ControlStateNames[i]);
      oCaption := FStateCaptions.GetState(ControlStateNames[i]);

      if Assigned(oObj) or Assigned(oFont) or Assigned(oCaption) then
      begin
        Lines.Add(TABS[4] + TGB+TAG_STATE+SPC+ATTR_NAME+ATB + EncodeXML(ControlStateNames[i]) + ATC);
        if oObj is TAHMBitmap then
          Lines.Add(TABS[5] + TGB+TAG_BITMAP+SPC+ATTR_NAME+ATB + EncodeXML(oObj.Name) + ATE)
        else if oObj is TAHMShape then
          Lines.Add(TABS[5] + TGB+TAG_SHAPE+SPC+ATTR_NAME+ATB + EncodeXML(oObj.Name) + ATE);
        if Assigned(oCaption) then
          Lines.Add(TABS[5] + TGB+TAG_CAPTION+SPC+ATTR_NAME+ATB + EncodeXML(oCaption.Name) + ATE);
        if Assigned(oFont) then
          Lines.Add(TABS[5] + TGB+TAG_FONT+SPC+ATTR_NAME+ATB + EncodeXML(oFont.Name) + ATE);
        Lines.Add(TABS[4] + TGE+TAG_STATE+TGC);
      end;
    end;
    Lines.Add(TABS[3] + TGE+TAG_STATES+TGC);
  end;

  // Do we need to store icons?
  if FStateIcons.IsDifferent(oAncs.StateIcons) then
  begin
    Lines.Add(TABS[3] + TGB+TAG_ICONS+TGC);
    for i := 0 to High(ControlStateNames) do
    begin
      oIcon := FStateIcons.GetState(ControlStateNames[i]);

      if Assigned(oIcon) then
      begin
        Lines.Add(TABS[4] + TGB+TAG_STATE+SPC+ATTR_NAME+ATB + EncodeXML(ControlStateNames[i]) + ATC);
        if oIcon is TAHMBitmap then
          Lines.Add(TABS[5] + TGB+TAG_BITMAP+SPC+ATTR_NAME+ATB + EncodeXML(oIcon.Name) + ATE)
        else if oIcon is TAHMShape then
          Lines.Add(TABS[5] + TGB+TAG_SHAPE+SPC+ATTR_NAME+ATB + EncodeXML(oIcon.Name) + ATE);
        Lines.Add(TABS[4] + TGE+TAG_STATE+TGC);
      end;
    end;
    Lines.Add(TABS[3] + TGE+TAG_ICONS+TGC);
  end;

  // Do we need to store icon properties?
  if ((FVirtIconX <> 0) and (FVirtIconX <> oAncs.VirtualIconX)) or
     ((FVirtIconY <> 0) and (FVirtIconY <> oAncs.VirtualIconY)) or
     ((FVirtIconWidth <> 0) and (FVirtIconWidth <> oAncs.VirtualIconWidth)) or
     ((FVirtIconHeight <> 0) and (FVirtIconHeight <> oAncs.VirtualIconHeight)) then
  begin
    Lines.Add(TABS[3] + TGB+TAG_ICON+TGC);
    // Do we need to store size?
    if ((FVirtIconWidth <> 0) and (FVirtIconWidth <> oAncs.VirtualIconWidth)) or
       ((FVirtIconHeight <> 0) and (FVirtIconHeight <> oAncs.VirtualIconHeight)) then
      Lines.Add(TABS[4] + TGB+TAG_SIZE+TGC+ IntToStr(FVirtIconWidth) + ',' +
                IntToStr(FVirtIconHeight) + TGE+TAG_SIZE+TGC);
    // Do we need to store position?
    if ((FVirtIconX <> 0) and (FVirtIconX <> oAncs.VirtualIconX)) or
       ((FVirtIconY <> 0) and (FVirtIconY <> oAncs.VirtualIconY)) then
      Lines.Add(TABS[4] + TGB+TAG_POSITION+TGC+ IntToStr(FVirtIconX) + ',' +
                IntToStr(FVirtIconY) + TGE+TAG_POSITION+TGC);
    Lines.Add(TABS[3] + TGE+TAG_ICON+TGC);
  end;

  // Do we need to store size?
  if ((VirtualWidth <> 0) and (VirtualWidth <> oAncs.VirtualWidth)) or
     ((VirtualHeight <> 0) and (VirtualHeight <> oAncs.VirtualHeight)) then
    Lines.Add(TABS[3] + TGB+TAG_SIZE+TGC+ IntToStr(VirtualWidth) + ',' +
              IntToStr(VirtualHeight) + TGE+TAG_SIZE+TGC);
end;

procedure TAHMButton.ParseFromXML(XML: TAHMXMLHelper);
var
  sCaption, sState, sDest: String;
  iX, iY: Integer;
  ButtonNode, PropsNode, StateNode, DetailNode: TAHMXMLNode;
begin
  // Initialise parsing nodes
  ButtonNode := XML.CurrentNode;
  PropsNode := nil;

  // All button elements are optional, so see what we have
  with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
    while XML.NextElement(ButtonNode, PropsNode) do
    begin
      if XML.CurrentElement = TAG_CAPTION then
      begin
        // Handle button captions
        sCaption := XML.GetAttribute(ATTR_NAME);
        if sCaption <> '' then Caption := CaptionList.GetCaption(sCaption);
        DetailNode := nil;

        while XML.NextElement(PropsNode, DetailNode) do
        begin
          // Check for caption position
          if XML.CurrentElement = TAG_POSITION then
          begin
            if XML.GetCoordsValue(iX, iY) then
            begin
              VirtualCaptionX := iX;
              VirtualCaptionY := iY;
            end
            else
              LogParsingError(THM_PRS_VIS_MALFORMED_BT_POSITION_TAGS, Self);
          end;
        end;
      end
      else if XML.CurrentElement = TAG_STATES then
      begin
        // Handle button states
        StateNode := nil;
        while XML.FindElement(PropsNode, StateNode, TAG_STATE) do
        begin
          sState := XML.GetAttribute(ATTR_NAME);
          DetailNode := nil;

          // We may have multiple state properties
          while XML.NextElement(StateNode, DetailNode) do
          begin
            // What type of object do we want to use for this state?
            sDest := XML.GetAttribute(ATTR_NAME);
            if XML.CurrentElement = TAG_SHAPE then
              States.AddObject(sState, ShapeList.GetShape(sDest))
            else if XML.CurrentElement = TAG_BITMAP then
              States.AddObject(sState, BitmapList.GetBitmap(sDest))
            else if XML.CurrentElement = TAG_FONT then
              StateFonts.AddObject(sState, FontList.GetFont(sDest))
            else if XML.CurrentElement = TAG_CAPTION then
              StateCaptions.AddObject(sState, CaptionList.GetCaption(sDest))
            else
              LogParsingError(THM_PRS_VIS_UNSUPPORTED_BT_STATE_TYPE, Self);
          end;
        end;
      end
      else if XML.CurrentElement = TAG_ICONS then
      begin
        // Handle button icons
        StateNode := nil;
        while XML.FindElement(PropsNode, StateNode, TAG_STATE) do
        begin
          sState := XML.GetAttribute(ATTR_NAME);
          DetailNode := nil;

          // We may have multiple state properties
          while XML.NextElement(StateNode, DetailNode) do
          begin
            // What type of object do we want to use for this state?
            sDest := XML.GetAttribute(ATTR_NAME);
            if XML.CurrentElement = TAG_SHAPE then
              StateIcons.AddObject(sState, ShapeList.GetShape(sDest))
            else if XML.CurrentElement = TAG_BITMAP then
              StateIcons.AddObject(sState, BitmapList.GetBitmap(sDest))
            else
              LogParsingError(THM_PRS_VIS_UNSUPPORTED_BT_ICON_TYPE, Self);
          end;
        end;
      end
      else if XML.CurrentElement = TAG_ICON then
      begin
        // Handle button icon properties
        while XML.NextElement(PropsNode, DetailNode) do
        begin
          if XML.CurrentElement = TAG_SIZE then
          begin
            if XML.GetCoordsValue(iX, iY) then
            begin
              VirtualIconWidth := iX;
              VirtualIconHeight := iY;
            end
            else
              LogParsingError(THM_PRS_VIS_MALFORMED_BT_ICON_SIZE_TAGS, Self);
          end
          else if XML.CurrentElement = TAG_POSITION then
          begin
            if XML.GetCoordsValue(iX, iY) then
            begin
              VirtualIconX := iX;
              VirtualIconY := iY;
            end
            else
              LogParsingError(THM_PRS_VIS_MALFORMED_BT_ICON_POS_TAGS, Self);
          end
          else
            LogParsingError(THM_PRS_VIS_UNSUPPORTED_BT_ICON_PROP, Self);
        end;
      end
      else if XML.CurrentElement = TAG_SIZE then
      begin
        if XML.GetCoordsValue(iX, iY) then
        begin
          VirtualWidth := iX;
          VirtualHeight := iY;
        end
        else
          LogParsingError(THM_PRS_VIS_MALFORMED_SIZE_TAGS, Self);
      end;
    end;
end;

procedure TAHMButton.InvalidateUIObject(UIObject: TAHMUIObject);
begin
  // Changes to shapes or bitmaps will invalidate the button
  if FStates.Contains(UIObject) then
    ApplicationManagerInstance.UIManager.InvalidateUIObject(Self);
end;

procedure TAHMButton.BuildProperties(Properties: TAHMUIPropertyList);
begin
{$IFDEF DESIGNER}
  inherited;

  // Build custom button properties
  Properties.Add('States', 'States', CAT_APPEARANCE, HINT_STATES);
  Properties.Add('Icons', 'StateIcons', CAT_APPEARANCE, HINT_ICONS);
  Properties.Add('Fonts', 'StateFonts', CAT_APPEARANCE, HINT_FONTS);
  Properties.Add('Captions', 'StateCaptions', CAT_APPEARANCE, HINT_CAPTIONS);
  Properties.Add('Default Caption', 'Caption', CAT_APPEARANCE, HINT_CAPTION);
  Properties.Add('Caption X', 'VirtualCaptionX', CAT_DESIGN, HINT_CAPTIONX);
  Properties.Add('Caption Y', 'VirtualCaptionY', CAT_DESIGN, HINT_CAPTIONY);
  Properties.Add('Icon X', 'VirtualIconX', CAT_DESIGN, HINT_ICONX);
  Properties.Add('Icon Y', 'VirtualIconY', CAT_DESIGN, HINT_ICONY);
  Properties.Add('Icon Width', 'VirtualIconWidth', CAT_DESIGN, HINT_ICONWIDTH);
  Properties.Add('Icon Height', 'VirtualIconHeight', CAT_DESIGN, HINT_ICONHEIGHT);
  Properties.Add('Width', 'VirtualWidth', CAT_DESIGN, HINT_WIDTH);
  Properties.Add('Height', 'VirtualHeight', CAT_DESIGN, HINT_HEIGHT);
{$ENDIF}
end;


end.
